它有一个可选参数,在执行时它会把可选参数加载进返回寄存器里,然后立刻执行返回命令,跳出当前栈帧。

点击按钮会暂停 app (这会运行  process interrupt  命令,因为 LLDB 总是在背后运行)。这会让你可以访问调试器,但看起来可以做的事情不多

用一个活动汇集响应某个用户交互行为或其他事件的全部工作,无论这些工作是同步的执行的还是被分配到其他队列和进程中去的

活动可以在跨队列跨进程的情况下帮助你追踪出导致崩溃发生的事件。面包屑可以帮你跨越多个活动画出导致崩溃发生的事件轨迹。最后,追踪信息可以帮助你为当前活动添加更详细的信息

DTrace 是一个提供了 zero disable cost 的动态追踪框架,也就是说当代码中的探针关闭时,不会有额外的资源消耗

provider:module:function:name

探针描述定义了语句匹配什么探针。所有的部分都可以省略,形式如下:

provider:module:function:name

如  syscall::*lwp*:entry  匹配所有  syscall  提供者的  entry ,并且函数名字包含  lwp  的探针

第二个语句在从 syscall 中返回时匹配。这个调用将和进入时是同一个线程,因此可以确定,即使有多个线程在同一时间进行系统调用, self->ts  也具有我们所期待的值

下表为所有集积函数:

DTrace 允许我们创建自己的探针,通过这个,我们可以为我们自己的 app 释放 DTrace 的真正威力

这些在 DTrace 中被称作静态探针

DTrace 静态探针的强大之处在于给我们提供了稳定的接口来调试我们的代码,并且即便是在生产代码中这个接口也是存在的。即使对于应用的生产版本,当有人看到奇怪的行为,我们也可以给正在运行的应用附加一段 DTrace 脚本

d 语言传值

sudo dtrace -q -s sample-timing-3.d -p 198

你可以将内置 (所谓静态的) 探针加入代码中。IA 探针看起来和普通的 C 函数非常相似

内置的  walltimestamp  变量返回当前时间

还有一个虚拟的单位为纳秒的时间戳变量, vtimestamp 

还有一个虚拟的单位为纳秒的时间戳变量, vtimestamp 。它表示当前的线程在 CPU 上运行的时间减去在 DTrace 上花费的时

如何将静态探针加入到我们的工

可以在 build phase 中添加一个 "Run Script",并勾选上 "Show environment variables in build log"。

定制 build phases 有一个非常好用的功能:添加带有水印(包括版本号和 commit hash)的 app icon -- 只需要在 build phase 中添加一个 "Run Script",并用下面的命令来获取版本号和 commit hash:

如果你希望自己或者别人编写的代码看起来比较简洁点,可以添加一个 "Run Script":如果一个源文件超过指定行数,就发出警告。如下代码所示,设置的行数为 200。

一条 build rule 指定了其应用于哪种类型文件,该类型文件是如何被处理的,以及输出的内容该如何处置

从编译、链接一直到 code signing 和 packaging。

LLVM 的优点主要得益于它的三层式架构 -- 第一层支持多种语言作为输入(例如 C, ObjectiveC, C++ 和 Haskell),第二层是一个共享式的优化器(对 LLVM IR 做优化处理),第三层是许多不同的目标平台(例如 Intel, ARM 和 PowerPC)

写是告诉编译器 MyClass 是一个类,并且在 .m 实现文件中可以通过 import  MyClass.h  的方式来使用它

执行以下预处理命令,看看是什么效果:

clang -E hello.c | less 接下来看看处理后的代码,一共是 401 行。

建议大家不要在需要预处理的代码中加入内联代码逻辑

因为这里定义的代码是内联的 (inlined),所以它的效率和宏变量差不多,但是可靠性比宏定义要好许多。再者,还可以设置断点、类型检查以及避免异常行为。

基本上,宏的最佳使用场景是日志输出,可以使用  __FILE__  和  __LINE__  和 assert 宏。

般会把类型分为两类:动态的和静态的。动态的在运行时做检查,静态的在编译时做检查。以往,编写代码时可以向任意对象发送任何消息,在运行时,才会检查对象是否能够响应这些消息。由于只是在运行时做此类检查,所以叫做动态类型

静态类型,是在编译时做检查。当在代码中使用 ARC 时,编译器在编译期间,会做许多的类型检查:因为编译器需要知道哪个对象该如何使用。

这就是为什么会有行为驱动开发 (BDD),它旨在解决具体问题,帮助开发人员确定应该测试些什么

让我们思考你设计的 app 中的一个对象。它有一个接口定义了其方法和依赖关系。这些方法和依赖,声明了你对象的约定。它们定义了如何与你应用的其他部分交互,以及它的功能是什么。它们定义了对象的行为

BDD DSL 鼓励弄清楚对象在给定条件下应该如何表现这一要求

以按意愿指定任意多的嵌套块,你可以基于对象或者它们的依赖关系的上下文来编写的不同的测试

重点是不需要阅读任何实际代码,因此它们减少了认知负荷。此外,它提供了一个标准语言来帮助你了解你团队的每一个成员,即便他们技术略

首先,在  describe  块内,这些字符串将联系紧密的被测试的一部分特性的行为进行分组描述 (例如,移动一辆汽车

double 可以理解为置换,它是所有模拟测试对象的统称

模拟方法并返回值

stub 可以理解为测试桩,它能实现当特定的方法被调用时,返回一个指定的模拟值

spy 可以理解为侦查,它负责汇报情况,持续追踪什么方法被调用了,以及调用过程中传递了哪些参数

模拟主义的方式是测试对象之间的交互。通过使用模拟对象,你可以更容易地验证被测对象是否遵循了它与其他类已建立的协议,使得在正确的时间发生正确的外部调用。

这种测试可以驱动出更好的生产代码,因为你需要明确模拟出特定的方法,这可以帮你设计出在两个对象之间使用的更优雅的API,这种想法与模拟驱动紧密联系在一起。

这种在你测试后再验证调用方法的模拟测试风格被认为是一种 “运行后验证” 的方式。

在执行测试代码前先设定对测试结果的期望。最后,你只需要验证期望和实际结果是否对应

期望运行的验证,在执行测试代码前先设定对测试结果的期望。最后,你只需要验证期望和实际结果是否对应

OCMock 也支持对类方法的 stub,你也可以用这种方式来测试,如果  doSomething  方法通过  [UIApplication sharedApplication]  来实现而不是  UIApplication  对象的注入初始化:

使用模拟主义风格书写测试的部分原因是通过这样的测试能比较容易的找到两个对象间最清晰可行的接口。

“若一个对象对于快速且可重复的测试有着直接影响,如何对一个依赖这种对象的方法进行单元测试呢

目前此类单元测试的最大障碍是,如何在你想要测试的代码之外的地方处理这种依赖关系。

由于人们经常会对特定实例存在着固有认识,所以还应尽量避免潜意识中对使用属性注入的倾向性。另外,请确定默认值不会引用到其他库的代码

基本上,构造器注入应该作为首选武器存在。其优势就是让所涉及的依赖非常清晰

它可能已经违背了单一职能原则。

属性注入的长处是将初始化与注入分离,这在不能改变调用者部分的时候非常有用

假如所依赖的对象针对每次调用都会有所不同的话,使用方法注入会比较好。

IB其实不仅仅是UI设计器,任何属性都可以通过将其声明为 IBOutlets 来赋值。当通过 IB 创建的 View 初始化的时候,可以一并创建通过 IB 声明的 Object (因为利用 IB 不仅可以创建 UI 对象还可以创建 “Object” (NSObject 类型,代表 icon 是个纯黄色立方体),那么当 IB 文件初始化的时候,IB 上定义的对象也会随之初始化

我还要讨论一下关于 DI 会导致破坏程序封装原则的这个问题。其实 DI 的目标就是让依赖更加明显。我们界定了组件的边界和它们之间的组装方式

以当我们在编写单元测试的时候用到 DI,应该回想一下上文所提到的更高层面的概念。请将可插拔模块牢记吧。它会影响你的很多设计决策,并且引导你去理解更多的 DI 模式和原则。

面所有的理由在某些方面是对的,但是这些理由适用于我们开发者的就是自动化测试能够让我们修改代

那是一个让测试失败 (或者通过) 的好的理由么?” 如果你发现那是一个让测试失败 (或者通过) 的不好的理由,那么请修正它。

那样,将来你修改你的代码的时候,你的测试只会因为好的理由而通过或者失败,这会比因为不好的理由而失败的古怪的测试得到的回报要好

因为你知道哪些测试失败了以及哪些代码的修改使得它们又通过了,你会很有信心,因为你只修改了你想要修改的部分。这就是自动化测试如何帮助我们通过修改代码来修改代码的行为的。

注意,看到一个测试失败是正常的,因为它是我们正在更新的行为相对应的测试。

么,我们如何测试一个类的私有方法呢?通过这个类的公有 API。永远通过公有 API 测试你的代码

同样,当我们 stub 一个方法的时候,我们必须依据它做出的约定来进行。但是私有方法没有指定的约定的 —— 毕竟,这也是为什么它们是私有的原因。由于私有方法的行为可以不经通知自行修改,你的 stub 可能与实际情况背道而驰,但是你的测试仍然会通过。

当私有方法被公开测试,后续又被其他人当作公共api 调用

应该做什么:将私有方法抽离到一个单独的类中,给这个类一个定义良好的约定,然后单独地测试它。当其他的测试代码依赖这个新的类的时候,如果有必要的话,你可以进行置换测试。

当私有方法被公开测试,后续又被其他人当作公共api 调用
正确方式通过共有api 提供的来测试私有api

程序的公有 API 定义了一个约定,它是一组关于你的程序对应于不同输入时定义良好的一组期望。

当我们 stub 一个方法的时候,我们必须依据它做出的约定来进行

当我们仅仅 stub 可能的机制中的一个的时候,问题就出现了。

应该做什么:以测试为目的,为同一个类创建可替代的实现,并将它作为置换来使用。

UIKit 在  UIControl  里提供了非常有用的  sendActionsForControlEvents:  方法,我们可以用来模仿用户操作。比如,用它来点击按钮

从接口的角度来看,我们完全可以手动地只使用日期来进行格式化。在这里,我们关心的重点是我们是否能正确获取字符串。我们需要测试的其实是这个行

这正是 BDD 的思想 -- 尝试思考行为的结果,而不是实际的实

我们利用 BDD DSL 嵌套能力来束缚模拟的多重行为 -- 我们首先模拟下载,接着,在相同的  describe  块内,我们模拟取消请求。

重点是不需要阅读任何实际代码,因此它们减少了认知负荷。此外,它提供了一个标准语言来帮助你了解你团队的每一个成员,即便他们技术略

    [self sendActionsForControlEvents:UIControlEventTouchUpInside];

            expect(button.allTargets).to.equal([NSSet setWithObject:signInViewController]);

    [self.target performSelector:self.action withObject:self];

就是在  XCTestCase  类中直接实现委托协议。通过这个方式,我们不用必须笨拙地 mock 这个 delegate。相反的,我们可以相当直接地与被测试的类互动。

就是在  XCTestCase  类中直接实现委托协议。通过这个方式,我们不用必须笨拙地 mock 这个 delegate。相反的,我们可以相当直接地与被测试的类互动。

通过实现代理方法来模拟数据